/*
* Copyright 2015 herd contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.finra.herd.service.advice;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.finra.herd.model.annotation.NamespacePermission;
import org.finra.herd.model.annotation.NamespacePermissions;
import org.finra.herd.model.api.xml.BusinessObjectDataNotificationFilter;
import org.finra.herd.model.api.xml.BusinessObjectDataNotificationRegistrationCreateRequest;
import org.finra.herd.model.api.xml.BusinessObjectDefinitionCreateRequest;
import org.finra.herd.model.api.xml.JobAction;
import org.finra.herd.model.api.xml.NamespaceAuthorization;
import org.finra.herd.model.api.xml.NamespacePermissionEnum;
import org.finra.herd.model.api.xml.NotificationRegistrationKey;
import org.finra.herd.model.dto.ApplicationUser;
import org.finra.herd.model.dto.SecurityUserWrapper;
import org.finra.herd.service.AbstractServiceTest;
public class NamespaceSecurityAdviceTest extends AbstractServiceTest
{
@Autowired
private NamespaceSecurityAdvice namespaceSecurityAdvice;
@After
public void after()
{
SecurityContextHolder.clearContext();
}
/**
* Asserts that the namespace security advice is enabled. Try calling a secured method with a mock user in the context with invalid permissions. The
* expectation is that the method call fails with AccessDeniedException if the advice is enabled.
*/
@Test
public void assertAdviceEnabled()
{
// put a fake user with no permissions into the security context
// the security context is cleared on the after() method of this test suite
String username = "username";
Class<?> generatedByClass = getClass();
ApplicationUser applicationUser = new ApplicationUser(generatedByClass);
applicationUser.setUserId(username);
applicationUser.setNamespaceAuthorizations(Collections.emptySet());
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(username, "password", false, false, false, false, Collections.emptyList(), applicationUser),
null));
try
{
businessObjectDefinitionService
.createBusinessObjectDefinition(new BusinessObjectDefinitionCreateRequest(NAMESPACE, BDEF_NAME, DATA_PROVIDER_NAME, null, null, null));
fail();
}
catch (Exception e)
{
assertEquals(AccessDeniedException.class, e.getClass());
}
}
/**
* Test case where the current user has both the namespace and the appropriate permissions.
*/
@Test
public void checkPermissionAssertNoExceptionWhenHasPermissions() throws Exception
{
// Mock a join point of the method call
// mockMethod("foo");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {"foo"});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("foo", Arrays.asList(NamespacePermissionEnum.READ)));
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
}
catch (AccessDeniedException e)
{
fail();
}
}
/**
* Test the case where user has the namespace but does not have the permission
*/
@Test
public void checkPermissionAssertAccessDeniedWhenCurrentUserHasWrongPermissionType() throws Exception
{
// Mock a join point of the method call
// mockMethod("foo");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {"foo"});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
// User has WRITE permissions, but the method requires READ
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("foo", Arrays.asList(NamespacePermissionEnum.WRITE)));
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
fail();
}
catch (Exception e)
{
assertEquals(AccessDeniedException.class, e.getClass());
assertEquals(String.format("User \"%s\" does not have \"[READ]\" permission(s) to the namespace \"foo\"", userId), e.getMessage());
}
}
@Test
public void checkPermissionAssertNoExceptionWhenNoSecurityContext() throws Exception
{
// Mock a join point of the method call
// mockMethod("foo");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {"foo"});
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
}
catch (AccessDeniedException e)
{
fail();
}
}
/**
* Test the case where user has the appropriate permission, but not for the namespace the method is invoked.
*/
@Test
public void checkPermissionAssertAccessDeniedWhenCurrentUserHasWrongNamespace() throws Exception
{
// Mock a join point of the method call
// mockMethod("foo");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {"foo"});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
// User has READ but for namespace "bar", not "foo"
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("bar", Arrays.asList(NamespacePermissionEnum.READ)));
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
fail();
}
catch (Exception e)
{
assertEquals(AccessDeniedException.class, e.getClass());
assertEquals(String.format("User \"%s\" does not have \"[READ]\" permission(s) to the namespace \"foo\"", userId), e.getMessage());
}
}
/**
* Test where a method is annotated with multiple NamespacePermission annotations. Asserts that the user will all permissions do not throw an exception.
*/
@Test
public void checkPermissionAssertNoExceptionWhenMultipleAnnotationsAndAllPermissionsValid() throws Exception
{
// Mock a join point of the method call
// mockMethodMultipleAnnotations("foo", "bar");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethodMultipleAnnotations", String.class, String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace1", "namespace2"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {"foo", "bar"});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("foo", Arrays.asList(NamespacePermissionEnum.READ)));
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("bar", Arrays.asList(NamespacePermissionEnum.WRITE)));
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
}
catch (AccessDeniedException e)
{
fail();
}
}
/**
* Test where a method with multiple annotation is called, but the user does not have permission to one of the namespaces. Asserts that the check throws
* AccessDenied.
*/
@Test
public void checkPermissionAssertAccessDeniedWhenMultipleAnnotationsAndUserHasOneWrongPermission() throws Exception
{
// Mock a join point of the method call
// mockMethodMultipleAnnotations("foo", "bar");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethodMultipleAnnotations", String.class, String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace1", "namespace2"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {"foo", "bar"});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("foo", Arrays.asList(NamespacePermissionEnum.READ)));
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
fail();
}
catch (Exception e)
{
assertEquals(AccessDeniedException.class, e.getClass());
assertEquals(String.format("User \"%s\" does not have \"[WRITE]\" permission(s) to the namespace \"bar\"", userId), e.getMessage());
}
}
@Test
public void checkPermissionAssertNoExceptionWhenComplexCaseAndUserHasAllPermissions() throws Exception
{
// Mock a join point of the method call
// mockMethod(request);
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", BusinessObjectDataNotificationRegistrationCreateRequest.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"request"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
BusinessObjectDataNotificationRegistrationCreateRequest request = new BusinessObjectDataNotificationRegistrationCreateRequest();
request.setBusinessObjectDataNotificationRegistrationKey(new NotificationRegistrationKey("ns1", null));
request.setBusinessObjectDataNotificationFilter(new BusinessObjectDataNotificationFilter("ns2", null, null, null, null, null, null, null));
request.setJobActions(Arrays.asList(new JobAction("ns3", null, null), new JobAction("ns4", null, null)));
when(joinPoint.getArgs()).thenReturn(new Object[] {request});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("ns1", Arrays.asList(NamespacePermissionEnum.WRITE)));
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("ns2", Arrays.asList(NamespacePermissionEnum.READ)));
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("ns3", Arrays.asList(NamespacePermissionEnum.EXECUTE)));
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("ns4", Arrays.asList(NamespacePermissionEnum.EXECUTE)));
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
}
catch (AccessDeniedException e)
{
fail();
}
}
@Test
public void checkPermissionAssertAccessDeniedWhenComplexCaseAndUserHasWrongPermission() throws Exception
{
// Mock a join point of the method call
// mockMethod(request);
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", BusinessObjectDataNotificationRegistrationCreateRequest.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"request"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
BusinessObjectDataNotificationRegistrationCreateRequest request = new BusinessObjectDataNotificationRegistrationCreateRequest();
request.setBusinessObjectDataNotificationRegistrationKey(new NotificationRegistrationKey("ns1", null));
request.setBusinessObjectDataNotificationFilter(new BusinessObjectDataNotificationFilter("ns2", null, null, null, null, null, null, null));
request.setJobActions(Arrays.asList(new JobAction("ns3", null, null), new JobAction("ns4", null, null)));
when(joinPoint.getArgs()).thenReturn(new Object[] {request});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("ns1", Arrays.asList(NamespacePermissionEnum.WRITE)));
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("ns2", Arrays.asList(NamespacePermissionEnum.READ)));
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("ns3", Arrays.asList(NamespacePermissionEnum.EXECUTE)));
// User does not have the expected EXECUTE permission on ns4
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("ns4", Arrays.asList(NamespacePermissionEnum.READ)));
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
fail();
}
catch (Exception e)
{
assertEquals(AccessDeniedException.class, e.getClass());
assertEquals(String.format("User \"%s\" does not have \"[EXECUTE]\" permission(s) to the namespace \"ns4\"", userId), e.getMessage());
}
}
@Test
public void checkPermissionAssertErrorWhenAnnotationFieldRefersToNonString() throws Exception
{
// Mock a join point of the method call
// mockMethod(1);
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", Integer.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"aNumber"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {1});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
fail();
}
catch (Exception e)
{
assertEquals(IllegalStateException.class, e.getClass());
assertEquals("Object must be of type class java.lang.String or interface java.util.Collection. Actual object.class = class java.lang.Integer",
e.getMessage());
}
}
@Test
public void checkPermissionAssertNoErrorWhenMethodDoesNotHaveAnnotations() throws Exception
{
// Mock a join point of the method call
// mockMethod(1);
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod");
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
}
catch (AccessDeniedException e)
{
fail();
}
}
@Test
public void checkPermissionAssertNoErrorWhenUserHasMultiplePermissions() throws Exception
{
// Mock a join point of the method call
// mockMethod("foo");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", String.class);
when(methodSignature.getMethod()).thenReturn(method);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {"foo"});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
applicationUser.getNamespaceAuthorizations()
.add(new NamespaceAuthorization("foo", Arrays.asList(NamespacePermissionEnum.READ, NamespacePermissionEnum.WRITE)));
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
}
catch (AccessDeniedException e)
{
fail();
}
}
@Test
public void checkPermissionAssertAccessDeniedWhenUserRequiresMultiplePermissionsButIsMissingOne() throws Exception
{
// Mock a join point of the method call
// mockMethodMultiplePermissions("foo");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethodMultiplePermissions", String.class);
when(methodSignature.getMethod()).thenReturn(method);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {"foo"});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
// User requires both READ and WRITE, but only has READ
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("foo", Arrays.asList(NamespacePermissionEnum.READ)));
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
fail();
}
catch (Exception e)
{
assertEquals(AccessDeniedException.class, e.getClass());
assertEquals(String.format("User \"%s\" does not have \"[READ, WRITE]\" permission(s) to the namespace \"foo\"", userId), e.getMessage());
}
}
@Test
public void checkPermissionAssertAccessDeniedWhenCurrentUserHasNullAuthorizations() throws Exception
{
// Mock a join point of the method call
// mockMethod("foo");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {"foo"});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(null);
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
fail();
}
catch (Exception e)
{
assertEquals(AccessDeniedException.class, e.getClass());
assertEquals(String.format("User \"%s\" does not have \"[READ]\" permission(s) to the namespace \"foo\"", userId), e.getMessage());
}
}
@Test
public void checkPermissionAssertAccessDeniedWhenCurrentUserHasNullPermissions() throws Exception
{
// Mock a join point of the method call
// mockMethod("foo");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {"foo"});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("foo", null));
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
fail();
}
catch (Exception e)
{
assertEquals(AccessDeniedException.class, e.getClass());
assertEquals(String.format("User \"%s\" does not have \"[READ]\" permission(s) to the namespace \"foo\"", userId), e.getMessage());
}
}
@Test
public void checkPermissionAssertAccessDeniedWhenApplicationUserIsNull() throws Exception
{
// Mock a join point of the method call
// mockMethod("foo");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {"foo"});
String userId = "userId";
SecurityContextHolder.getContext()
.setAuthentication(new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), null), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
fail();
}
catch (Exception e)
{
assertEquals(AccessDeniedException.class, e.getClass());
assertEquals("Current user does not have \"[READ]\" permission(s) to the namespace \"foo\"", e.getMessage());
}
}
@Test
public void checkPermissionAssertAccessDeniedWhenPrincipalIsNotSecurityUserWrapper() throws Exception
{
// Mock a join point of the method call
// mockMethod("foo");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {"foo"});
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("streetcreds", null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
fail();
}
catch (Exception e)
{
assertEquals(AccessDeniedException.class, e.getClass());
assertEquals("Current user does not have \"[READ]\" permission(s) to the namespace \"foo\"", e.getMessage());
}
}
@Test
public void checkPermissionAssertAccessDeniedWhenPrincipalIsNull() throws Exception
{
// Mock a join point of the method call
// mockMethod("foo");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {"foo"});
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken(null, null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
fail();
}
catch (Exception e)
{
assertEquals(AccessDeniedException.class, e.getClass());
assertEquals("Current user does not have \"[READ]\" permission(s) to the namespace \"foo\"", e.getMessage());
}
}
@Test
public void checkPermissionAssertNoExceptionWhenHasPermissionsNamespaceIgnoreCase() throws Exception
{
// Mock a join point of the method call
// mockMethod("foo");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {"foo"});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
// user has permission to capital "FOO" and needs permission to lowercase "foo"
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("FOO", Arrays.asList(NamespacePermissionEnum.READ)));
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
}
catch (AccessDeniedException e)
{
fail();
}
}
@Test
public void checkPermissionAssertNoExceptionWhenNamespaceBlank() throws Exception
{
// Mock a join point of the method call
// mockMethod(" ");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {BLANK_TEXT});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
}
catch (AccessDeniedException e)
{
fail();
}
}
@Test
public void checkPermissionAssertNoExceptionWhenHasPermissionsNamespaceTrimmed() throws Exception
{
// Mock a join point of the method call
// mockMethod(" foo ");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {BLANK_TEXT + "foo" + BLANK_TEXT});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
// User has permission to "foo" but the actual namespace given is " foo "
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("foo", Arrays.asList(NamespacePermissionEnum.READ)));
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
}
catch (AccessDeniedException e)
{
fail();
}
}
@Test
public void checkPermissionAssertAccessDeniedWhenNoPermissionsNamespaceTrimmed() throws Exception
{
// Mock a join point of the method call
// mockMethod(" foo ");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {BLANK_TEXT + "foo" + BLANK_TEXT});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
// User has permission to "bar" but the actual namespace given is " foo "
applicationUser.getNamespaceAuthorizations().add(new NamespaceAuthorization("bar", Arrays.asList(NamespacePermissionEnum.READ)));
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
fail();
}
catch (Exception e)
{
assertEquals(AccessDeniedException.class, e.getClass());
assertEquals(String.format("User \"%s\" does not have \"[READ]\" permission(s) to the namespace \"foo\"", userId), e.getMessage());
}
}
@Test
public void checkPermissionAssertMultipleAccessDeniedExceptionsAreGatheredIntoSingleMessageWhenMultipleAnnotations() throws Exception
{
// Mock a join point of the method call
// mockMethodMultipleAnnotations("namespace1", "namespace2");
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethodMultipleAnnotations", String.class, String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace1", "namespace2"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {"foo", "bar"});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
// User has no permissions
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
fail();
}
catch (Exception e)
{
assertEquals(AccessDeniedException.class, e.getClass());
assertEquals(String.format("User \"%s\" does not have \"[READ]\" permission(s) to the namespace \"foo\"%n" +
"User \"%s\" does not have \"[WRITE]\" permission(s) to the namespace \"bar\"", userId, userId), e.getMessage());
}
}
@Test
public void checkPermissionAssertMultipleAccessDeniedExceptionsAreGatheredIntoSingleMessageWhenCollections() throws Exception
{
// Mock a join point of the method call
// mockMethod({"", ""})
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", List.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespaces"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {Arrays.asList("foo", "bar")});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
// User has no permissions
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
fail();
}
catch (Exception e)
{
assertEquals(AccessDeniedException.class, e.getClass());
assertEquals(String.format("User \"%s\" does not have \"[READ]\" permission(s) to the namespace \"foo\"%n" +
"User \"%s\" does not have \"[READ]\" permission(s) to the namespace \"bar\"", userId, userId), e.getMessage());
}
}
/**
* Assert no access denied exception when parameter value is null.
*/
@Test
public void checkPermissionAssertNoExceptionWhenNull() throws Exception
{
// Mock a join point of the method call
// mockMethod(null);
JoinPoint joinPoint = mock(JoinPoint.class);
MethodSignature methodSignature = mock(MethodSignature.class);
Method method = NamespaceSecurityAdviceTest.class.getDeclaredMethod("mockMethod", String.class);
when(methodSignature.getParameterNames()).thenReturn(new String[] {"namespace"});
when(methodSignature.getMethod()).thenReturn(method);
when(joinPoint.getSignature()).thenReturn(methodSignature);
when(joinPoint.getArgs()).thenReturn(new Object[] {null});
String userId = "userId";
ApplicationUser applicationUser = new ApplicationUser(getClass());
applicationUser.setUserId(userId);
applicationUser.setNamespaceAuthorizations(new HashSet<>());
SecurityContextHolder.getContext().setAuthentication(
new TestingAuthenticationToken(new SecurityUserWrapper(userId, "", false, false, false, false, Arrays.asList(), applicationUser), null));
try
{
namespaceSecurityAdvice.checkPermission(joinPoint);
}
catch (AccessDeniedException e)
{
fail();
}
}
/**
* Do not invoke this method. This method is a test input for reflection related tests.
*/
@NamespacePermission(fields = "#namespace", permissions = NamespacePermissionEnum.READ)
private void mockMethod(String namespace)
{
}
/**
* Do not invoke this method. This method is a test input for reflection related tests. Case where namespace1 requires READ, but namespace2 requires WRITE
*/
@NamespacePermissions({@NamespacePermission(fields = "#namespace1", permissions = NamespacePermissionEnum.READ), @NamespacePermission(
fields = "#namespace2", permissions = NamespacePermissionEnum.WRITE)})
private void mockMethodMultipleAnnotations(String namespace1, String namespace2)
{
}
/**
* Do not invoke this method. This method is a test input for reflection related tests. The most complex case we have so far: a mock simulation of
* notification registration, where there are 3 different permissions on namespaces, and one of them is a collection.
*/
@NamespacePermissions({@NamespacePermission(fields = "#request.businessObjectDataNotificationRegistrationKey.namespace",
permissions = NamespacePermissionEnum.WRITE), @NamespacePermission(fields = "#request.businessObjectDataNotificationFilter.namespace",
permissions = NamespacePermissionEnum.READ), @NamespacePermission(fields = "#request.jobActions.![namespace]",
permissions = NamespacePermissionEnum.EXECUTE)})
private void mockMethod(BusinessObjectDataNotificationRegistrationCreateRequest request)
{
}
/**
* Do not invoke this method. This method is a test input for reflection related tests. A case where the method is annotated, but the field refers to a
* non-string parameter.
*/
@NamespacePermission(fields = "#aNumber", permissions = NamespacePermissionEnum.READ)
private void mockMethod(Integer aNumber)
{
}
/**
* Do not invoke this method. This method is a test input for reflection related tests. Case where a method is not annotated.
*/
@SuppressWarnings("unused")
private void mockMethod()
{
}
/**
* Do not invoke this method. This method is a test input for reflection related tests. A case which the current user requires multiple permissions on the
* same namespace to access.
*/
@NamespacePermission(fields = "#namespace", permissions = {NamespacePermissionEnum.READ, NamespacePermissionEnum.WRITE})
private void mockMethodMultiplePermissions(String namespace)
{
}
/**
* Do not invoke this method. This method is a test input for reflection related tests.
*/
@NamespacePermission(fields = "#namespaces", permissions = {NamespacePermissionEnum.READ})
private void mockMethod(List<String> namespaces)
{
}
}